/*
 * Copyright (c) 2006-2021, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2010-10-01     Yi.Qiu      first version
 */

 /*
  * Warning, this keypad driver can only work on QEMU emulator
  */

#include <rtthread.h>
#include <s3c24x0.h>
#include <rthw.h>

#define KEY_RX_BUFFER_SIZE      32

struct rt_key_device
{
    struct rt_device parent;

    rt_uint32_t  rx_buffer[KEY_RX_BUFFER_SIZE];
    rt_uint32_t read_index, save_index;
};
static struct rt_key_device *key_device = RT_NULL;

/* save a char to serial buffer */
static void rt_key_savechar(char ch)
{
    rt_base_t level;

    /* disable interrupt */
    level = rt_hw_interrupt_disable();

    key_device->rx_buffer[key_device->save_index] = ch;
    key_device->save_index ++;
    if (key_device->save_index >= KEY_RX_BUFFER_SIZE)
        key_device->save_index = 0;

    /* if the next position is read index, discard this 'read char' */
    if (key_device->save_index == key_device->read_index)
    {
        key_device->read_index ++;
        if (key_device->read_index >= KEY_RX_BUFFER_SIZE)
            key_device->read_index = 0;
    }

    /* enable interrupt */
    rt_hw_interrupt_enable(level);
}

/* ISR for serial interrupt */
static void rt_hw_key_isr(void)
{
    /* save on rx buffer */
    rt_key_savechar(URXH1 & 0xff);

    /* invoke callback */
    if (key_device->parent.rx_indicate != RT_NULL)
    {
        rt_size_t rx_length;

        /* get rx length */
        rx_length = key_device->read_index > key_device->save_index ?
            KEY_RX_BUFFER_SIZE - key_device->read_index + key_device->save_index :
            key_device->save_index - key_device->read_index;

        key_device->parent.rx_indicate(&key_device->parent, rx_length);
    }
}

/**
 * This function is only for QEMU emulation
 */
static void rt_key_handler(int vector)
{
    INTSUBMSK |= (BIT_SUB_RXD1);

    rt_hw_key_isr();

    SUBSRCPND |= BIT_SUB_RXD1;

    /*Unmask sub interrupt (RXD0)*/
    INTSUBMSK  &=~(BIT_SUB_RXD1);
}

/**
 * This function is only for QEMU emulation
 */
static void key_init(void)
{
    int i = 0;

    GPHCON |= 0xa0;
    /*PULLUP is enable */
    GPHUP  |= 0x0c;

    /* FIFO enable, Tx/Rx FIFO clear */
    UFCON1 = 0x0;
    /* disable the flow control */
    UMCON1= 0x0;
    /* Normal,No parity,1 stop,8 bit */
    ULCON1 = 0x3;
    /*
     * tx=level,rx=edge,disable timeout int.,enable rx error int.,
     * normal,interrupt or polling
     */
    UCON1 = 0x245;

    //UBRD0 = div;
    // UBRD0 = 0x500;   /* baudrate = 19200bps */
    UBRD1 = 0x1a;

    UTXH1 = 0x2;
    URXH1 = 0x1;

    /* output PCLK to UART0/1, PWMTIMER */
    CLKCON |= 0x0D00;

    for (i = 0; i < 100; i++);

    /* install key isr */
    INTSUBMSK &= ~(BIT_SUB_RXD1);

    rt_hw_interrupt_install(INTUART1, rt_key_handler, RT_NULL , "INTUART1");
    rt_hw_interrupt_umask(INTUART1);
}

static rt_err_t rt_key_init(rt_device_t dev)
{
    if (!(dev->flag & RT_DEVICE_FLAG_ACTIVATED))
    {

        if (dev->flag & RT_DEVICE_FLAG_INT_RX)
        {
            rt_memset(key_device->rx_buffer, 0,
                sizeof(key_device->rx_buffer));
            key_device->read_index = key_device->save_index = 0;
        }

        dev->flag |= RT_DEVICE_FLAG_ACTIVATED;
    }

    return RT_EOK;
}

static rt_err_t rt_key_open(rt_device_t dev, rt_uint16_t oflag)
{
    return RT_EOK;
}

static rt_err_t rt_key_close(rt_device_t dev)
{
    return RT_EOK;
}

static rt_size_t rt_key_read (rt_device_t dev, rt_off_t pos, void* buffer, rt_size_t size)
{
    rt_uint8_t* ptr;
    rt_err_t err_code;
    rt_base_t level;

    ptr = buffer;
    err_code = RT_EOK;

    /* interrupt mode Rx */
    while (size)
    {
        if (key_device->read_index != key_device->save_index)
        {
            *ptr++ = key_device->rx_buffer[key_device->read_index];
            size --;

            /* disable interrupt */
            level = rt_hw_interrupt_disable();

            key_device->read_index ++;
            if (key_device->read_index >= KEY_RX_BUFFER_SIZE)
                key_device->read_index = 0;

            /* enable interrupt */
            rt_hw_interrupt_enable(level);
        }
        else
        {
            /* set error code */
            err_code = -RT_EEMPTY;
            break;
        }
    }

    /* set error code */
    rt_set_errno(err_code);
    return (rt_uint32_t)ptr - (rt_uint32_t)buffer;
}

static rt_err_t rt_key_control(rt_device_t dev, int cmd, void *args)
{
    return RT_EOK;
}

#ifdef RT_USING_RTGUI
#include <rtgui/event.h>
#include <rtgui/rtgui_server.h>
#include <rtgui/kbddef.h>

static int s_key_map[0xff] = {0};

static void rt_keymap_init(void)
{
    s_key_map[0x1] = RTGUIK_ESCAPE;
    s_key_map[0xc] = RTGUIK_MINUS;
    s_key_map[0x39] = RTGUIK_SPACE;
    s_key_map[0xd] = RTGUIK_KP_EQUALS;
    s_key_map[0xe] = RTGUIK_BACKSPACE;
    s_key_map[0xf] = RTGUIK_TAB;
    s_key_map[0x1c] = RTGUIK_KP_ENTER;
    s_key_map[0xb] = RTGUIK_0;
    s_key_map[0x2] = RTGUIK_1;
    s_key_map[0x3] = RTGUIK_2;
    s_key_map[0x4] = RTGUIK_3;
    s_key_map[0x5] = RTGUIK_4;
    s_key_map[0x6] = RTGUIK_5;
    s_key_map[0x7] = RTGUIK_6;
    s_key_map[0x8] = RTGUIK_7;
    s_key_map[0x9] = RTGUIK_8;
    s_key_map[0xa] = RTGUIK_9;
    s_key_map[0x3b] = RTGUIK_F1;
    s_key_map[0x3c] = RTGUIK_F2;
    s_key_map[0x3d] = RTGUIK_F3;
    s_key_map[0x3e] = RTGUIK_F4;
    s_key_map[0xef] = RTGUIK_F5;
    s_key_map[0x40] = RTGUIK_F6;
    s_key_map[0x41] = RTGUIK_F7;
    s_key_map[0x42] = RTGUIK_F8;
    s_key_map[0x43] = RTGUIK_F9;
    s_key_map[0x1e] = RTGUIK_a;
    s_key_map[0x30] = RTGUIK_b;
    s_key_map[0x2c] = RTGUIK_c;
    s_key_map[0x20] = RTGUIK_d;
    s_key_map[0x12] = RTGUIK_e;
    s_key_map[0x21] = RTGUIK_f;
    s_key_map[0x22] = RTGUIK_g;
    s_key_map[0x23] = RTGUIK_h;
    s_key_map[0x17] = RTGUIK_i;
    s_key_map[0x24] = RTGUIK_j;
    s_key_map[0x25] = RTGUIK_k;
    s_key_map[0x26] = RTGUIK_l;
    s_key_map[0x32] = RTGUIK_m;
    s_key_map[0x31] = RTGUIK_n;
    s_key_map[0x18] = RTGUIK_o;
    s_key_map[0x19] = RTGUIK_p;
    s_key_map[0x10] = RTGUIK_q;
    s_key_map[0x13] = RTGUIK_r;
    s_key_map[0x1f] = RTGUIK_s;
    s_key_map[0x14] = RTGUIK_t;
    s_key_map[0x16] = RTGUIK_u;
    s_key_map[0x2f] = RTGUIK_v;
    s_key_map[0x11] = RTGUIK_w;
    s_key_map[0x2d] = RTGUIK_x;
    s_key_map[0x15] = RTGUIK_y;
    s_key_map[0x2c] = RTGUIK_z;
    s_key_map[0x4b] = RTGUIK_LEFT;
    s_key_map[0x4d] = RTGUIK_RIGHT;
    s_key_map[0x50] = RTGUIK_DOWN;
    s_key_map[0x2e] = RTGUIK_DELETE;
    s_key_map[0x48] = RTGUIK_UP;
}

static rt_err_t rtgui_key_rx(rt_device_t dev, rt_size_t size)
{
    struct rtgui_event_kbd kbd_event;
    char key_value;

    while(rt_device_read(dev, 0, &key_value, 1) == 1)
    {
        /* init keyboard event */
        RTGUI_EVENT_KBD_INIT(&kbd_event);
        kbd_event.mod  = RTGUI_KMOD_NONE;
        kbd_event.unicode = 0;
        kbd_event.key = RTGUIK_UNKNOWN;

        if(key_value &  0x80)
        {
            kbd_event.type = RTGUI_KEYUP;
        }
        else
        {
            kbd_event.type = RTGUI_KEYDOWN;
        }

        kbd_event.key = s_key_map[key_value & 0x7F];
    }
    if (kbd_event.key != RTGUIK_UNKNOWN)
    {
        /* post down event */
        rtgui_server_post_event(&(kbd_event.parent), sizeof(kbd_event));
    }

    return RT_EOK;
}

#endif

/*
 * key driver register
 */
void rt_hw_key_init(void)
{
    /* hardware init */
    key_init();

    key_device = (struct rt_key_device*)rt_malloc (sizeof(struct rt_key_device));
    if (key_device == RT_NULL) return; /* no memory yet */

    /* clear device structure */
    rt_memset(&(key_device->parent), 0, sizeof(struct rt_device));

    key_device->parent.type         = RT_Device_Class_Char;
    key_device->parent.tx_complete = RT_NULL;
    key_device->parent.init         = rt_key_init;
    key_device->parent.open     = rt_key_open;
    key_device->parent.close        = rt_key_close;
    key_device->parent.read         = rt_key_read;
    key_device->parent.write        = RT_NULL;
    key_device->parent.control  = rt_key_control;
    key_device->parent.user_data   = RT_NULL;

#ifdef RT_USING_RTGUI
    key_device->parent.rx_indicate = rtgui_key_rx;

    /* init keymap */
    rt_keymap_init();
#endif

    /* register key device to RT-Thread */
    rt_device_register(&(key_device->parent), "key", RT_DEVICE_FLAG_RDONLY | RT_DEVICE_FLAG_INT_RX);
}

